test(contract): sandbox tests for the attestation flow against the real tee-verifier - #4110
test(contract): sandbox tests for the attestation flow against the real tee-verifier#4110pbeza wants to merge 43 commits into
Conversation
|
This PR includes changes to source code files in Suggested title: |
This comment was marked as outdated.
This comment was marked as outdated.
b5e55c3 to
faea559
Compare
…tion The MPC node generates its NEAR signer key inside the enclave and writes it only to the CVM's encrypted disk. Collecting test assets needs that key alongside the quote it is bound to, and an app-compose pre-launch script is the only way in: the dev image ships no sshd and the guest agent serves only node info and container logs. vmm-cli already supports --prelaunch-script; this just plumbs an optional PRELAUNCH_SCRIPT through. Attestation verification rejects any app-compose carrying a script, so a CVM deployed this way cannot join a network - the deploy guide, default.env and the script itself now say so loudly.
Collecting the attestation fixtures requires a pre-launch script to export the node's in-enclave signer key, which makes the resulting app-compose one that production verification rejects as arbitrary root code. The new allow-pre-launch-script feature relaxes exactly that field for test builds; init_script and bash_script stay rejected, and the production wasm never enables it. The check takes the policy as an argument so the strict behaviour keeps its unit test in test builds too, and a further test pins the compiled-in policy at the call site.
…ner key Regenerated from a fresh localnet CVM on a TDX host, keeping the node's NEAR signer key this time so tests can sign submit_participant_info as the fixture node. The quote's report_data binds that key and the contract reads it from the transaction signer, so without it the Verified store path is unreachable (#3787). create-assets.sh had been producing collateral our own parser cannot read since the tee-verifier-interface refactor: nodes serialize the DER and signature fields as JSON byte arrays while collateral_from_json expects hex strings. It now hex-encodes those fields, and strips the NUL that terminates the PEM chains so the fixture stays valid PEM. The regeneration reminder is unconditional, since the committed key is normally present but stale. .editorconfig no longer asserts the borsh fixture's final byte: it is whatever the encoded collateral ends with.
… released artifacts Implying allow-pre-launch-script from test-utils meant anything wanting only the collateral_from_json parser also disabled the arbitrary-root-code check. The feature is now named on the dependency edges that build tests, so every enablement is greppable, and a new check fails the build if it reaches the feature graph of the contract wasm, the node, attestation-cli (which verifies attestations locally) or the verifier wasm. Two tests replace one that could not fail: the committed keypair is checked against near_account_public_key.pub, since create-assets.sh rewrites the public key but cannot rewrite the secret; and the fixture app-compose is pinned as satisfying the production policy once its export hook is removed, which is the only place real attestation data meets the strict check. Also drops doc claims about sandbox tests and a contract feature that live in the follow-up, and records why the signer key has to be exported rather than supplied to the node.
Replaces the helper that took the compile-time policy as a runtime argument with `PRE_LAUNCH_SCRIPT_ALLOWED`, so every app-compose invariant reads inline in `validate_app_compose_config`. Drops the relaxation from this crate's own dev-dependency edge, so its unit tests compile the production check. The two tests that only existed to pass both values of the former argument go with it; CI builds `--all-features`, so the strict branch was never compiled there anyway, and the feature-leak guard is what keeps the relaxation out of released artifacts.
Multi-path updates replace the eight per-field pipeline stages, the hex alphabet is bound once, and splitting on the NUL replaces the trim loop. Records why the two committed fixtures encode the same bytes differently: `public_data.json` is the endpoint response verbatim, so its byte fields are JSON arrays, while the fixture parser reads hex.
Restores the Given/When/Then split in the fixture test, and stops the localnet runbook from restating the collateral's `nextUpdate` date, which goes stale on regeneration.
Removes `scripts/check-attestation-feature-leak.sh` and its `check-all-fast` task. The comments that named it as the control now state the invariant it checked: the relaxation is enabled only on dependency edges that no released artifact builds. Also corrects the `local-verify` comment, which claimed `mpc-contract` enables it; the off-chain callers are the node, the attestation CLI and the TEE authority.
Adds `export-signer-key-prelaunch.sh`, the `PRELAUNCH_SCRIPT` that exports the node's in-enclave signer key, so the extraction is reproducible instead of recoverable only from the fixture's measured app-compose. `single-node.sh` now resolves a relative `PRELAUNCH_SCRIPT` against the repo root: `deploy-launcher.sh` reads it from its own directory, so the documented path was looked up two directories away and the deploy failed. The runbook drops the steps duplicated from the asset README and gains the paths the exported key actually lands in.
…hook The key-export hook drops to 16 lines: the copy into the CVM's shared dir could never work, since that mount is read-only in the guest, so the console echo is the only channel. The comment header goes too, as the hook is embedded verbatim in the measured app-compose. Collected on a TDX host with the fixture's existing image digests, so the OS and image measurements are unchanged and only the app-compose-derived values move: `rtmr3`, `report_data`, the keys and the collateral. `mrtd` and `rtmr0`-`rtmr2` still match `mpc-attestation`'s compiled-in dev measurements. `VALID_ATTESTATION_TIMESTAMP` moves into the new collateral's validity window, which now opens after the previous pinned value.
Nothing else in these scripts rewrites a user-supplied relative path: `BASE_PATH` is used verbatim, and `ENV_TPL`/`CONF_TPL` only default to paths under the repo root. Resolving this one made it behave unlike its neighbours, so the runbook passes an absolute path instead and `deploy-launcher.sh` fails fast when it does not exist. That leaves `single-node.sh` unchanged from main: re-exporting the variable was a no-op, since a value set on the command line is already in the environment that `deploy-launcher.sh` inherits. Also drops the commented-out `PRELAUNCH_SCRIPT` from `default.env`, which the collection flow never reads — `single-node.sh` overrides `--env-file` — and trims the warning at the point of use.
Measured rather than guessed: disabling the feature and running without `--all-features` fails six `validated_dstack_*` tests in `mpc-attestation`, two in `attestation-cli`, and five in `mpc-contract`, since the regenerated fixture's app-compose carries the export hook. Noted where they cluster — two test-file headers, the shared `dstack_verification_setup` helper, and one test — rather than repeated on all thirteen. Also renames the fixture test to say the hook is cleared, and rewords the feature comments that read awkwardly.
The feature sat on `test-utils`' normal dependency edge, so any invocation whose root set includes that crate — `cargo build --workspace --release`, for one — unified it into the single `attestation` lib that `mpc-node` and the contract link. Nothing ships that way today, but a comment was the only thing saying so. It now forwards through `mpc-contract` and `attestation-cli` features enabled only where those crates build their own tests, matching what `mpc-attestation` already did. `test-utils` no longer needs it: its one verification call is the DCAP step, which never reaches app-compose validation. Also fixes the `VALID_ATTESTATION_TIMESTAMP` comment, which still named the previous value's date, and restores the `local-verify` documentation dropped earlier in the branch.
| /// (spelled in full because the import is feature-gated). The pin exists | ||
| /// because sandbox chain time is wall-clock and forward-only, so it can never | ||
| /// fall inside the validity window of a checked-in collateral fixture. | ||
| fn now_seconds() -> u64 { |
There was a problem hiding this comment.
Production compiles this down to just the block timestamp, so the gating itself is right. But sandbox-test-hooks is a public feature, so a --all-features build turns it on — and here that means expired collateral verifies.
Worth making it unbuildable rather than just unused:
#[cfg(all(target_arch = "wasm32", feature = "sandbox-test-hooks"))]
compile_error!("never enable in a wasm build");4b7ea55 to
ab45f90
Compare
…y method The fixture is captured from a CVM whose launcher compose carries the service that exported its signer key, so its compose hash is not derivable from the compiled-in template and no vote can allow it. `sandbox-test-attestation` now gates a method that whitelists that one hash, replacing the app-compose policy relaxation the feature used to enable.
`verify_quote` takes `&self`, so near-sdk reads the STATE key on every call: production builds never read the pinned-timestamp key, but they do read storage. The E2E task runner builds five things, not three, and passes three WASM paths. The design doc's status line pointed at the shipped design as if it were the superseded one.
59555e8 to
4e6f821
Compare
This comment was marked as low quality.
This comment was marked as low quality.
…tion-cross-contract
…tion-cross-contract
This comment was marked as low quality.
This comment was marked as low quality.
This comment was marked as low quality.
This comment was marked as low quality.
|
@claude review |
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as low quality.
This comment was marked as low quality.
The verifier's sandbox-test-hooks feature now takes effect only together with --cfg mpc_sandbox_wasm, which only the test harness sets on the wasm it builds; a wasm carrying the feature without the marker fails to compile. The contract-side sandbox-test-attestation feature and its whitelisting method are gone: sandbox tests patch the fixture's compose hash straight into contract state through a host-only test-utils helper, so the deployed contract is the production-shape build. Two new tests pin the released behavior: the production verifier ignores the pinned timestamp, and a verified quote whose compose hash was never whitelisted stores nothing.
|
PR title type suggestion: This PR changes source code files (e.g., Suggested title: |
…tion-cross-contract
|
PR title type suggestion: This PR modifies source code files across multiple crates, so the type prefix should probably be Suggested title: |
|
PR title type suggestion: This PR modifies source code files across multiple crates, so the type should be Suggested title: |
…tion-cross-contract
The crate convention is a test_utils submodule per domain module, and the helper edits the launcher allowlist, which lives under tee. Also drops the round-trip assert: borsh encoding is canonical, so deserializing and reserializing the same type is byte-identical by construction.
|
PR title type suggestion: This PR includes changes to source code files in addition to test files. The |
Collapses the repeated setup and voting scaffolding: setup() and setup_verified_fixture() return the handles the tests bind anyway, all_vote() takes the vote to cast, prepay_grant_from_separate_payer() returns the balance its callers read next, and pin_verifier_clock() replaces the patch_state blocks. Reuses submit_dstack, get_config and cluster_poll_retry where the code hand-rolled them, and drops the sha2 dependency for the near-kit hash already used a few lines away.
…tion-cross-contract
|
This PR adds new crates ( Suggested title: |
…helper Every lookup in the file asks for the same TLS key, so keep the key in one place instead of restating it at each call site.
|
Closing in favor of #4165. |
Closes #4084